package com.jt.aop;

import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ShardedJedis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component  //代表我是一个javaBean
@Aspect     //代表我是一个切面
public class CacheAop {

    @Autowired
    //private Jedis jedis;
    //private ShardedJedis jedis;
    private JedisCluster jedis;

    /**
     * Aop缓存实现的业务策略
     * 1.切入点表达式应该拦截  @CacheFind注解
     * 2.通知方法： 环绕通知
     *  如果是使用环绕通知，则必须在第一个参数的位置添加 ProceedingJoinPoint
     *
     *  动态获取注解参数的步骤：
     *  1.@annotation(cacheFind)   切入点表达式要求拦截一个类型为cacheFind的注解
     *  2.并且利用连接点为参数中的cacheFind 赋值
     */

    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){
        try {
            Object result =null;
            //1.动态获取注解中的数据
            String prekey = cacheFind.key();
            //2.动态获取方法中的参数；
            String args = Arrays.toString(joinPoint.getArgs());
            String key = prekey + "::" +args;
            //3.校验redis中是否有数据
            if (jedis.exists(key)){
                //有缓存，从redis中获取数据，并转换为对象返回
                String JSON = jedis.get(key);
                //target代表这目标方法的返回值类型......
                //动态获取目标方法的返回值类型??   向上造型 不用强转   向下造型
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
                Class returnType = methodSignature.getReturnType();
                //将json数据转化为对象
                result = ObjectMapperUtil.toObject(JSON, returnType);
                System.out.println("AOP redis缓存查询");
            }else {
                //当redis中没有数据时，查询数据库
                result = joinPoint.proceed();    //执行目标方法
                System.out.println("AOP执行数据库操作");
                //将对象转换成JSON
                String json = ObjectMapperUtil.toJson(result);
                if (cacheFind.seconds()>0){
                    jedis.setex(key,cacheFind.seconds(),json);
                }else {
                    jedis.set(key, json);
                }
            }
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
                throw new RuntimeException(throwable);  //将检查异常转化为运行时异常
        }
    }

















    /*//1.定义切入点表达式
    @Pointcut("bean(itemCatServiceImpl)") //只拦截XXX类中的方法
    public void pointCut(){}

    *//**
     * 2.定义通知方法
     * 需求:
     *  1.想获取目标方法名称
     *  2.获取目标方法对象
     *  3.获取用户传递的参数
     *//*
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("我是Before前置通知");
        //1.获取类名称
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        //2.获取对象
        Object target = joinPoint.getTarget();
        //3.获取参数
        Object[] objs = joinPoint.getArgs();
        System.out.println("类名名称:"+className);
        System.out.println("方法名称:"+methodName);
        System.out.println("对象名称:"+target);
        System.out.println("方法参数:"+objs);
    }*/
}
